package com.paas.demo.config;

import com.paas.demo.shiro.ShiroCredentialsMatcher;
import com.paas.demo.shiro.ShiroCustomRealm;
import com.paas.demo.shiro.ShiroSessionManager;
import com.paas.demo.shiro.ShiroSessionRedisDao;
import org.apache.shiro.cache.CacheManager;
import org.apache.shiro.cache.MemoryConstrainedCacheManager;
import org.apache.shiro.crypto.hash.Md5Hash;
import org.apache.shiro.realm.Realm;
import org.apache.shiro.session.mgt.SessionManager;
import org.apache.shiro.spring.web.config.DefaultShiroFilterChainDefinition;
import org.apache.shiro.spring.web.config.ShiroFilterChainDefinition;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.apache.shiro.web.servlet.SimpleCookie;
import org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
 * @ClassName : ShiroConfig
 * @Description : 服务端Shiro权限框架配置
 * @author : pengjie
 * @email  : 627799251@qq.com
 * @version  v1.0
 * @Date : 2019/1/21 12:56
*/
@Configuration
public class ShiroConfig {

    /**
     * 这里统一做鉴权，即判断哪些请求路径需要用户登录，哪些请求路径不需要用户登录。
     * 这里只做鉴权，不做权限控制，因为权限用注解来做。
     * @return
     */
    @Bean
    public ShiroFilterChainDefinition shiroFilterChainDefinition() {
        DefaultShiroFilterChainDefinition chain = new DefaultShiroFilterChainDefinition();
        //哪些请求可以匿名访问
        chain.addPathDefinition("/app/v1/login", "anon");
        chain.addPathDefinition("/page/401", "anon");
        chain.addPathDefinition("/page/403", "anon");
        chain.addPathDefinition("/t5/hello", "anon");
        chain.addPathDefinition("/t5/guest", "anon");
        chain.addPathDefinition("/druid/**", "anon");

        //swagger接口权限 开放
        chain.addPathDefinition("/swagger-ui.html", "anon");
        chain.addPathDefinition("/webjars/**", "anon");
        chain.addPathDefinition("/v2/**", "anon");
        chain.addPathDefinition("/swagger-resources/**", "anon");

        //除了以上的请求外，其它请求都需要登录
        chain.addPathDefinition("/**", "anon");
        return chain;
    }

    //注入自定义的realm，告诉shiro如何获取用户信息来做登录或权限控制
    @Bean
    public Realm realm() {
        ShiroCustomRealm shiroRealm = new ShiroCustomRealm();

        //告诉shiro如何根据获取到的用户信息中的密码和盐值来校验密码
        ShiroCredentialsMatcher credentialsMatcher = new ShiroCredentialsMatcher();
        //散列算法:这里使用MD5算法
        credentialsMatcher.setHashAlgorithmName(Md5Hash.ALGORITHM_NAME);
        //散列的次数，比如散列两次，相当于 md5(md5(""))
        credentialsMatcher.setHashIterations(1024);
        //默认是true，此时用的是密码加密用的是Hex编码；false时用Base64编码
        credentialsMatcher.setStoredCredentialsHexEncoded(true);

        shiroRealm.setCredentialsMatcher(credentialsMatcher);

        return shiroRealm;
    }

    @Bean
    public static DefaultAdvisorAutoProxyCreator getDefaultAdvisorAutoProxyCreator() {
        DefaultAdvisorAutoProxyCreator creator = new DefaultAdvisorAutoProxyCreator();
        /**
         * setUsePrefix(false)用于解决一个奇怪的bug。在引入spring aop的情况下。
         * 在@Controller注解的类的方法中加入@RequiresRole注解，会导致该方法无法映射请求，导致返回404。
         * 加入这项配置能解决这个bug
         */
        creator.setUsePrefix(true);
        return creator;
    }

    /**
     * 配置保存sessionId的cookie
     * 注意：这里的cookie 不是上面的rememberMe cookie rememberMe需要一个cookie session管理
     * @return
     */
    @Bean("sessionIdCookie")
    public SimpleCookie sessionIdCookie(){
        //这个参数是cookie的名称
        SimpleCookie simpleCookie = new SimpleCookie("BUSISESSIONID");
        //setcookie的httponly属性如果设为true的话，会增加对xss防护的安全系数。它有以下特点：

        //setcookie()的第七个参数
        //设为true后，只能通过http访问，javascript无法访问
        //防止xss读取cookie
        simpleCookie.setHttpOnly(true);
        simpleCookie.setPath("/");
        //maxAge=-1表示浏览器关闭时失效此Cookie
        simpleCookie.setMaxAge(-1);

        return simpleCookie;
    }

    /**
     * 自定义sessionManager
     * @return
     */
    @Bean
    public SessionManager sessionManager(){
        ShiroSessionManager shiroSessionManager = new ShiroSessionManager();
        //这里可以不设置。Shiro有默认的session管理。如果缓存为Redis则需改用Redis的管理
        shiroSessionManager.setSessionDAO(new ShiroSessionRedisDao());
        shiroSessionManager.setSessionIdCookie(sessionIdCookie());
        shiroSessionManager.setGlobalSessionTimeout(1800000);

        return shiroSessionManager;
    }
    /**
    @Bean
    public SimpleCookie rememberMeCookie(){
        //这个参数是cookie的名称
        SimpleCookie rememberMeCookie = new SimpleCookie("rememberMe");
        //setcookie的httponly属性如果设为true的话，会增加对xss防护的安全系数。它有以下特点：

        //setcookie()的第七个参数
        //设为true后，只能通过http访问，javascript无法访问
        //防止xss读取cookie
        rememberMeCookie.setHttpOnly(true);
        //rememberMeCookie.setPath("/");
        //maxAge=-1表示浏览器关闭时失效此Cookie
        rememberMeCookie.setMaxAge(24 * 60 * 60);

        return rememberMeCookie;
    }
    @Bean
    public CookieRememberMeManager rememberMeManager(){
        CookieRememberMeManager rememberMeManager = new CookieRememberMeManager();
        rememberMeManager.setCookie(rememberMeCookie());

        return rememberMeManager;
    }
    **/
    /**
     * 配置管理层。即安全控制层
     * @return
     */
    @Bean
    public DefaultWebSecurityManager securityManager(){
        DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
        securityManager.setRealm(realm());
        //自定义session管理
        securityManager.setSessionManager(sessionManager());
        //rememberMe管理
        //securityManager.setRememberMeManager(rememberMeManager());
        //自定义缓存实现
        securityManager.setCacheManager(cacheManager());
        return  securityManager;
    }

    @Bean
    protected CacheManager cacheManager() {
        return new MemoryConstrainedCacheManager();
    }
}
